package com.ctg.itrdc.imageconsole.control.ssh.socket;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ctg.itrdc.imageconsole.common.util.PropertiesUtils;
import com.ctg.itrdc.imageconsole.control.ssh.SecureShellJsonpAction;
import com.ctg.itrdc.imageconsole.control.ssh.db.ClusterConfigDB;
import com.ctg.itrdc.imageconsole.control.ssh.db.UserDB;
import com.ctg.itrdc.imageconsole.control.ssh.model.SchSession;
import com.ctg.itrdc.imageconsole.control.ssh.model.UserSchSessions;
import com.ctg.itrdc.imageconsole.control.ssh.task.SentOutputTask;
import com.ctg.itrdc.imageconsole.control.ssh.utils.AuthUtil;
import com.ctg.itrdc.imageconsole.control.ssh.utils.SessionOutputUtil;
import com.ctg.itrdc.imageconsole.data.entity.ClusterConfig;
import com.google.gson.Gson;

/**
 * 
 * @description: 监测连接是否打开、接收命令并执行、关闭连接(提供给开发者门户前端跨域调用)
 * @version: v1.0.0
 * @author: Piao.Wu
 * 
 *          Modification History: Date Author Version Description
 *          -----------------------------------------------------------------
 *          2016年11月18日 上午11:34:55 Piao.Wu v1.0.0 create
 *
 */
@ServerEndpoint(value = "/sshjsonp/terms.ws/{instanceId}/{clusterCode}/{podId}", configurator = GetHttpSessionConfigurator.class)
public class SecureShellJsonpWS {

	private static Logger logger = LoggerFactory.getLogger(SecureShellJsonpWS.class);

	private HttpSession httpSession;
	private Session session;
	private Long sessionId = null;

	@OnOpen
	public void onOpen(@PathParam("instanceId") Integer instanceId, @PathParam("clusterCode") String clusterCode,
			@PathParam("podId") String podId, Session session, EndpointConfig config) {
		logger.info("成功建立连接！");
		String websocketTimeout = PropertiesUtils.getConfigValue("sshConfig", "websocketTimeout");
		if (StringUtils.isNotEmpty(websocketTimeout)) {
			session.setMaxIdleTimeout(Long.parseLong(websocketTimeout) * 60000);
		} else {
			session.setMaxIdleTimeout(0);
		}
		this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
		this.sessionId = AuthUtil.getSessionId(httpSession);
		this.session = session;

		Runnable run = new SentOutputTask(sessionId, session, UserDB.getUser(AuthUtil.getUserId(httpSession)));
		Thread thread = new Thread(run);
		thread.start();
		// 连接打开后，根据instanceId、podId和apiServerUrl进行跳转，跳转到具体集群的具体pod容器中
		skipToContainer(instanceId, clusterCode, podId);
	}

	private void skipToContainer(Integer instanceId, String clusterCode, String podId) {
		logger.info("instanceId={}, clusterCode={}, podId={}", instanceId, clusterCode, podId);
		ClusterConfig clusterConfig = ClusterConfigDB.getClusterConfig(null, clusterCode);
		if (clusterConfig == null) {
			logger.error("skipToContainer方法： 没有获取到集群信息");
			return;
		}
		logger.info("ApiServerUrl=="+clusterConfig.getApiServerUrl());
		String command = "kubectl exec -it " + podId + " /bin/bash --server=http://" + clusterConfig.getApiServerUrl();
		onMessage("{'instanceId':" + instanceId + ",'command':'" + command + "','keyCode':-1}");
		onMessage("{'instanceId':" + instanceId + ",'command':'','keyCode':13}");
	}

	@SuppressWarnings("rawtypes")
	@OnMessage
	public void onMessage(String message) {
		logger.info("接收到客户端的消息！");
		if (session.isOpen() && StringUtils.isNotEmpty(message)) {
			logger.info("message:" + message);
			Map jsonRoot = new Gson().fromJson(message, Map.class);
			String command = (String) jsonRoot.get("command");
			Double keyCodeDbl = (Double) jsonRoot.get("keyCode");
			Integer keyCode = keyCodeDbl != null?keyCodeDbl.intValue():null;
			Double instanceId = (Double) jsonRoot.get("instanceId");
			UserSchSessions userSchSessions = SecureShellJsonpAction.getUserSchSessionMap().get(sessionId);
			if (userSchSessions != null) {
				logger.info("userSchSessions 不等于 null");
				SchSession schSession = userSchSessions.getSchSessionMap().get(instanceId.intValue());
				if (keyMap.containsKey(keyCode)) {
					try {
						schSession.getCommander().write(keyMap.get(keyCode));
					} catch (IOException ex) {
						logger.error(ex.toString(), ex);
					}
				} else {
					schSession.getCommander().print(command);
				}
			}
			// update timeout
			AuthUtil.setTimeout(httpSession);
		}
	}

	@OnClose
	public void onClose() {
		logger.info("客户端与服务端连接已断开");
		if (SecureShellJsonpAction.getUserSchSessionMap() != null) {
			UserSchSessions userSchSessions = SecureShellJsonpAction.getUserSchSessionMap().get(sessionId);
			if (userSchSessions != null) {
				Map<Integer, SchSession> schSessionMap = userSchSessions.getSchSessionMap();
				for (Integer sessionKey : schSessionMap.keySet()) {
					SchSession schSession = schSessionMap.get(sessionKey);
					// disconnect ssh session
					schSession.getChannel().disconnect();
					schSession.getSession().disconnect();
					schSession.setChannel(null);
					schSession.setSession(null);
					schSession.setInputToChannel(null);
					schSession.setCommander(null);
					schSession.setOutFromChannel(null);
					schSession = null;
					// remove from map
					schSessionMap.remove(sessionKey);
				}
				// clear and remove session map for user
				schSessionMap.clear();
				SecureShellJsonpAction.getUserSchSessionMap().remove(sessionId);
				SessionOutputUtil.removeUserSession(sessionId);
			}
		}
	}

	/**
	 * Maps key press events to the ascii values
	 */
	static Map<Integer, byte[]> keyMap = new HashMap<>();

	static {
		// ESC
		keyMap.put(27, new byte[] { (byte) 0x1b });
		// ENTER
		keyMap.put(13, new byte[] { (byte) 0x0d });
		// LEFT
		keyMap.put(37, new byte[] { (byte) 0x1b, (byte) 0x4f, (byte) 0x44 });
		// UP
		keyMap.put(38, new byte[] { (byte) 0x1b, (byte) 0x4f, (byte) 0x41 });
		// RIGHT
		keyMap.put(39, new byte[] { (byte) 0x1b, (byte) 0x4f, (byte) 0x43 });
		// DOWN
		keyMap.put(40, new byte[] { (byte) 0x1b, (byte) 0x4f, (byte) 0x42 });
		// BS
		keyMap.put(8, new byte[] { (byte) 0x7f });
		// TAB
		keyMap.put(9, new byte[] { (byte) 0x09 });
		// CTR
		keyMap.put(17, new byte[] {});
		// DEL
		keyMap.put(46, "\033[3~".getBytes());
		// CTR-A
		keyMap.put(65, new byte[] { (byte) 0x01 });
		// CTR-B
		keyMap.put(66, new byte[] { (byte) 0x02 });
		// CTR-C
		keyMap.put(67, new byte[] { (byte) 0x03 });
		// CTR-D
		keyMap.put(68, new byte[] { (byte) 0x04 });
		// CTR-E
		keyMap.put(69, new byte[] { (byte) 0x05 });
		// CTR-F
		keyMap.put(70, new byte[] { (byte) 0x06 });
		// CTR-G
		keyMap.put(71, new byte[] { (byte) 0x07 });
		// CTR-H
		keyMap.put(72, new byte[] { (byte) 0x08 });
		// CTR-I
		keyMap.put(73, new byte[] { (byte) 0x09 });
		// CTR-J
		keyMap.put(74, new byte[] { (byte) 0x0A });
		// CTR-K
		keyMap.put(75, new byte[] { (byte) 0x0B });
		// CTR-L
		keyMap.put(76, new byte[] { (byte) 0x0C });
		// CTR-M
		keyMap.put(77, new byte[] { (byte) 0x0D });
		// CTR-N
		keyMap.put(78, new byte[] { (byte) 0x0E });
		// CTR-O
		keyMap.put(79, new byte[] { (byte) 0x0F });
		// CTR-P
		keyMap.put(80, new byte[] { (byte) 0x10 });
		// CTR-Q
		keyMap.put(81, new byte[] { (byte) 0x11 });
		// CTR-R
		keyMap.put(82, new byte[] { (byte) 0x12 });
		// CTR-S
		keyMap.put(83, new byte[] { (byte) 0x13 });
		// CTR-T
		keyMap.put(84, new byte[] { (byte) 0x14 });
		// CTR-U
		keyMap.put(85, new byte[] { (byte) 0x15 });
		// CTR-V
		keyMap.put(86, new byte[] { (byte) 0x16 });
		// CTR-W
		keyMap.put(87, new byte[] { (byte) 0x17 });
		// CTR-X
		keyMap.put(88, new byte[] { (byte) 0x18 });
		// CTR-Y
		keyMap.put(89, new byte[] { (byte) 0x19 });
		// CTR-Z
		keyMap.put(90, new byte[] { (byte) 0x1A });
		// CTR-[
		keyMap.put(219, new byte[] { (byte) 0x1B });
		// CTR-]
		keyMap.put(221, new byte[] { (byte) 0x1D });
		// INSERT
		keyMap.put(45, "\033[2~".getBytes());
		// PG UP
		keyMap.put(33, "\033[5~".getBytes());
		// PG DOWN
		keyMap.put(34, "\033[6~".getBytes());
		// END
		keyMap.put(35, "\033[4~".getBytes());
		// HOME
		keyMap.put(36, "\033[1~".getBytes());

	}
}
